今天主要會講的部分有
Lifetime 生命週期是為了確保我們在需要參考的時候,它們都是有效的。
我們前面提到參考的時候,沒有說到每個參考其實都有生命週期,這是決定該參考是否有效的作用域。大多情況下生命週期是隱式且可推導出來的,就像大多情況下型別是可推導出來的。當多種型別都有可能時,我們就得詮釋型別。同樣地,當生命週期的參考能以不同方式關聯的話,我們就得詮釋生命週期。
舉個例子來說
fn main() {
let r;
{
let x = 5;
r = &x;
}
println!("r: {}", r);
}
在這裡,在內圈的scope,宣告了一個初始值為5的x變數,然後我們把參考賦值給r,在外層我們嘗試要印出這個r的時候就會失敗,因爲x指向的值已經被釋放.
為了檢查出這個錯誤,Rust 編譯器有個借用檢查器(borrow checker)會比較作用域來檢測所有的借用是否有效,如下
fn main() {
let r; // ---------+-- 'a
// |
{ // |
let x = 5; // -+-- 'b |
r = &x; // | |
} // -+ |
// |
println!("r: {}", r); // |
} // ---------+
這裡,x的生命週期 b 比 r 的生命週期 a 來的短,然後 r 又需要 x 的參考,所以就會有編譯錯誤.
那在考慮底下的這種情況
fn longest(x: &str, y: &str) -> &str {
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let string1 = String::from("abcd");
let string2 = "xyz";
let result = longest(string1.as_str(), string2);
println!("最長的字串為 {}", result);
}
在這裡 我們引入了兩個參考,但因為我們有一個判斷式,因為回傳值不固定,也就無法確定回傳值的生命週期,這時候我們就要去定義參考之間的關係來解決這個問題.
程式碼應該要修改如下才行
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let string1 = String::from("abcd");
let string2 = "xyz";
let result = longest(string1.as_str(), string2);
println!("最長的字串為 {}", result);
}
當我們向 longest
傳入實際參考時,'a
實際替代的生命週期為 x
作用域與 y
作用域重疊的部分。換句話說,泛型生命週期 'a
取得的生命週期會等於 x
與 y
的生命週期中較短的。因為我們將回傳的參考詮釋了相同的生命週期參數 'a
,回傳參考的生命週期也會保證在 x
和 y
的生命週期較短的結束前有效。
生命週期也是 Rust 相當重要的一個觀念,可以透過編譯器,多加練習後就能掌握什麼時候需要寫,什麼時候不需要寫了.
簡單來說,Trait 想像成一種 interface 或是抽象類別 abstract class 的概念,也就是從多種類型中抽取出共性的屬性或方法,並定義這些方法的規範.
底下舉個例子說明
首先我們先定義 trait,輸出是否可以執行
trait Callable {
fn execute(&self) {
println!("execute!!!");
}
}
接著,我們用impl
關鍵字幫 Struct 加上這個方法
impl Callable for Dataclient {}
impl Callable for Tradeclient {}
假設我們已經設定好變數,就可以像底下這樣使用trait中定義好的方法
dataclient.execute();
tradeclient.execute();
此外,trait還可以做到Polymorphism的效果,例如底下
fn Free(client: &dyn Callable) {
client.execute();
}
Free(&dataclient);
因為 dataclient 有這個特徵,所以可以被執行,&dyn 還可以透過 + 去增加更多條件的限制.
今天就先講到這,明天會繼續介紹 Enum 跟 Generics.